require("bit")
require("BinTalk/struct")

ProtocolReader = {}

function ProtocolReader.typeDsize(b, p)
    local bb = struct.unpack("B", string.sub(b, p + 1, p + 1))
    p = p + 1
    --local n = (bb & 0XC0) >> 6
    local n = bit.rshift(bit.band(bb, 0XC0), 6)
    --local s = bb & 0X3F
    local s = bit.band(bb, 0X3F)
    while (n > 0) do
        bb = struct.unpack("B", string.sub(b, p + 1, p + 1))
        p = p + 1
        --s = (s << 8) | bb
        s = bit.bor(bit.lshift(s, 8), bb)
        n = n - 1
    end
    return s, p
end

function ProtocolReader.typeNumber(f, n, b, p)
    return struct.unpack(f, string.sub(b, p + 1, p + n + 1)), p + n
end

function ProtocolReader.typeInt64(b, p)
    return ProtocolReader.typeNumber("<l", 8, b, p)
end

function ProtocolReader.typeUint64(b, p)
    return ProtocolReader.typeNumber("<L", 8, b, p)
end

function ProtocolReader.typeDouble(b, p)
    return ProtocolReader.typeNumber("<d", 8, b, p)
end

function ProtocolReader.typeFloat(b, p)
    return ProtocolReader.typeNumber("<f", 4, b, p)
end

function ProtocolReader.typeInt32(b, p)
    return ProtocolReader.typeNumber("<i", 4, b, p)
end

function ProtocolReader.typeUint32(b, p)
    return ProtocolReader.typeNumber("<I", 4, b, p)
end

function ProtocolReader.typeInt16(b, p)
    return ProtocolReader.typeNumber("<h", 2, b, p)
end

function ProtocolReader.typeUint16(b, p)
    return ProtocolReader.typeNumber("<H", 2, b, p)
end

function ProtocolReader.typeInt8(b, p)
    return ProtocolReader.typeNumber("<b", 1, b, p)
end

function ProtocolReader.typeUint8(b, p)
    return ProtocolReader.typeNumber("<B", 1, b, p)
end

function ProtocolReader.typeBool(b, p)
    if string.sub(b, p + 1, p + 1) == "\000" then
        return false, p + 1
    else
        return true, p + 1
    end
end

function ProtocolReader.typeString(b, p, valMax)
    local l, p = ProtocolReader.typeDsize(b, p)
    if l > valMax then
        error("typeString")
    end
    return string.sub(b, p + 1, p + l), p + l
end

function ProtocolReader.typeEnum(b, p, valMax)
    local e, p = ProtocolReader.typeUint8(b, p, 0)
    if e > valMax then
        error("typeEnum")
    end
    return e, p
end

function ProtocolReader.typeBinary(b, p, valMax)
    local l, p = ProtocolReader.typeDsize(b, p)
    if l > valMax then
        error("typeBinary")
    end
    return string.sub(b, p + 1, p + l), p + l
end

function ProtocolReader.readMid(b, p)
    return ProtocolReader.typeUint16(b, p, 0)
end

function ProtocolReader.read(b, p, t, arrMax, valMax)
    if arrMax > 0 then
        local s, p = ProtocolReader.typeDsize(b, p)
        if s > arrMax then
            error(read)
        else
            local arr = {}
            while s > 0 do
                i, p = t(b, p, valMax)
                table.insert(arr, i)
                s = s - 1
            end
            return arr, p
        end
    else
        return t(b, p, valMax)
    end
end
